<!DOCTYPE HTML>
<html>

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
	<link rel="shortcut icon" type="image/png" href="../static/img/bird-sm.png">

	<title>MockingBird Web Server</title>

	<script src="{{ url_for('static',filename='js/recorder-core.js') }}"></script>
	<script src="{{ url_for('static',filename='js/mp3.js') }}"></script>
	<script src="{{ url_for('static',filename='js/wav.js') }}"></script>
	<script src="{{ url_for('static',filename='js/mp3-engine.js') }}"></script>
	<script src="{{ url_for('static',filename='js/frequency.histogram.view.js') }}"></script>
	<script src="{{ url_for('static',filename='js/lib.fft.js') }}"></script>

	<script src="{{ url_for('static',filename='js/jquery.js') }}"></script>
</head>

<body>

	<div class="main">

		<div class="mainBox">
			<div class="title" >
				<div style="width: 15%;float: left;margin-left: 5%;">
					<img src="../static/img/bird.png" style="width: 100%;border-radius:50%;"></img>
				</div>
				<div style="width: 80% ;height: 15%;; margin-left: 15%;overflow: hidden;">
					<div style="margin-left: 5%;margin-top: 15px;font-size: xx-large;font-weight: bolder;">
						拟声鸟工具箱
					</div>
					<div style="margin-left: 5%;margin-top: 3px;font-size: large;">
						<a href="https://github.com/babysor/MockingBird" target="_blank">https://github.com/babysor/MockingBird</a>
					</div>
				</div>
			</div>	

			<div  style="margin-left: 5%;margin-top: 50px;width: 90%;">
				<div style="font-size: larger;font-weight: bolder;">1. 请输入中文</div>
				<textarea  id="user_input_text"
					style="border:1px solid #ccc; width: 100%; height: 100px; font-size: 15px; margin-top: 10px;"></textarea>
			</div>
			<div class="pd btns" style="margin-left: 5%;margin-top: 20px;width: 90%; ">
				<!-- <div>
				<button onclick="recOpen()" style="margin-right:10px">打开录音,请求权限</button>
				<button onclick="recClose()" style="margin-right:0">关闭录音,释放资源</button>
			</div> -->
				<div style="font-size: larger;font-weight: bolder;">2. 请直接录音，点击停止结束</div>
				<button onclick="recStart()" >录制</button>
				<button onclick="recStop()">停止</button>
				<button onclick="recPlay()" >播放</button>
			</div>
			<div class="pd btns" style="margin-left: 5%;margin-top: 20px;width: 90%; ">
				<div style="font-size: larger;font-weight: bolder;">或上传音频</div>
				<input type="file" id="fileInput" accept=".wav" />
				<label for="fileInput">选择音频</label>
				<div id="audio1"></div>
			</div>
			<div class="pd btns" style="margin-left: 5%;margin-top: 20px;width: 90%; ">
				<div style="font-size: larger;font-weight: bolder;">3. 选择Synthesizer模型</div>
				<span class="box">
					<select id="select">
					</select>
				</span>
			</div>
			<div class="pd btns" style="margin-left: 5%;margin-top: 20px;width: 90%; text-align:right;">
				<button id="upload" onclick="recUpload()">上传合成</button>
			</div>
			
			<!-- 波形绘制区域 -->
			<!-- <div class="pd recpower">
				<div style="height:40px;width:100%;background:#fff;position:relative;">
					<div class="recpowerx" style="height:40px;background:#ff3295;position:absolute;"></div>
					<div class="recpowert" style="padding-left:50px; line-height:40px; position: relative;"></div>
				</div>
			</div> -->
			<!-- <div class="pd waveBox" style="height:100px;">
				<div style="border:1px solid #ccc;display:inline-block; width: 100%; height: 100px;">
					<div style="height:100px; width: 100%; background-color: #5da1f5; position: relative;left: 0px;top: 0px;z-index: 10;"
						class="recwave"></div>
					<div
						style="background-color: transparent;position: relative;top: -80px;left: 30%;z-index: 20;font-size: 48px;color: #fff;">
						音频预览</div>
				</div>
			</div> -->
			<div class="reclog" style="margin-left: 5%;margin-top: 20px;width: 90%;"></div>
		</div>
	</div>


	<script>

		$("#fileInput").change(function(){
			var file = $("#fileInput").get(0).files;
			if (file.length > 0) {
				var path = URL.createObjectURL(file[0]);
				var audio = document.createElement('audio');
				audio.src = path;
				audio.controls = true;
				$('#audio1').empty().append(audio); 
			}
		});
		
		fetch("/api/synthesizers", {
			method: 'get',
			headers: {
				"X-CSRFToken": "{{ csrf_token() }}"
			}
		}).then(function (res) {
			if (!res.ok) throw Error(res.statusText);
			return res.json();
		}).then(function (data) {
			for (var synt of data) {
				var option = document.createElement('option');
				option.text = synt.name
				option.value = synt.path
				$("#select").append(option);
			}
		}).catch(function (err) {
			console.log('Error: ' + err.message);
		})

		var rec, wave, recBlob;
		/**调用open打开录音请求好录音权限**/
		var recOpen = function () {//一般在显示出录音按钮或相关的录音界面时进行此方法调用，后面用户点击开始录音时就能畅通无阻了
			rec = null;
			wave = null;
			recBlob = null;
			var newRec = Recorder({
				type: "wav", bitRate: 16, sampleRate: 16000
				, onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) {
					//录音实时回调，大约1秒调用12次本回调
					// document.querySelector(".recpowerx").style.width = powerLevel + "%";
					// document.querySelector(".recpowert").innerText = bufferDuration + " / " + powerLevel;

					//可视化图形绘制
					// wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
				}
			});

			createDelayDialog(); //我们可以选择性的弹一个对话框：为了防止移动端浏览器存在第三种情况：用户忽略，并且（或者国产系统UC系）浏览器没有任何回调，此处demo省略了弹窗的代码
			newRec.open(function () {//打开麦克风授权获得相关资源
				dialogCancel(); //如果开启了弹框，此处需要取消

				rec = newRec;

				//此处创建这些音频可视化图形绘制浏览器支持妥妥的
				// wave = Recorder.FrequencyHistogramView({ elem: ".recwave" });

				reclog("已打开录音，可以点击录制开始录音了", 2);
			}, function (msg, isUserNotAllow) {//用户拒绝未授权或不支持
				dialogCancel(); //如果开启了弹框，此处需要取消
				reclog((isUserNotAllow ? "UserNotAllow，" : "") + "打开录音失败：" + msg, 1);
			});

			window.waitDialogClick = function () {
				dialogCancel();
				reclog("打开失败：权限请求被忽略，<span style='color:#f00'>用户主动点击的弹窗</span>", 1);
			};
		};



		/**关闭录音，释放资源**/
		function recClose() {
			if (rec) {
				rec.close();
				reclog("已关闭");
			} else {
				reclog("未打开录音", 1);
			};
		};



		/**开始录音**/
		function recStart() {//打开了录音后才能进行start、stop调用
			if (rec && Recorder.IsOpen()) {
				recBlob = null;
				rec.start();
				reclog("已开始录音...");
			} else {
				reclog("未打开录音，请求录音权限，如已允许录音权限，请再次点击录制", 1);
				recOpen();
			};
		};

		function recStop() {
			rec.stop(function (blob, duration) {
				rec.close();//释放录音资源
				console.log(blob, (window.URL || webkitURL).createObjectURL(blob), "时长:" + duration + "ms");
				recBlob = blob;
				reclog("已录制wav：" + duration + "ms " + blob.size + "字节，可以点击播放、上传了", 2);
			}, function (msg) {
				reclog("录音失败:" + msg, 1);
			});
		};


		/**播放**/
		function recPlay() {
			if (!recBlob) {
				reclog("请先录音，然后停止后再播放", 1);
				return;
			};
			var cls = ("a" + Math.random()).replace(".", "");
			reclog('播放中: <span class="' + cls + '"></span>');
			var audio = document.createElement("audio");
			audio.controls = true;
			document.querySelector("." + cls).appendChild(audio);
			//简单利用URL生成播放地址，注意不用了时需要revokeObjectURL，否则霸占内存
			audio.src = (window.URL || webkitURL).createObjectURL(recBlob);
			audio.play();

			setTimeout(function () {
				(window.URL || webkitURL).revokeObjectURL(audio.src);
			}, 5000);
		};

		function playResult(resultBlob) {
			if (!resultBlob) {
				reclog("服务端出错，请重试", 1);
				return;
			};
			var cls = ("a" + Math.random()).replace(".", "");
			reclog('播放中: <span class="' + cls + '"></span>');
			var audio = document.createElement("audio");
			audio.controls = true;
			document.querySelector("." + cls).appendChild(audio);
			//简单利用URL生成播放地址，注意不用了时需要revokeObjectURL，否则霸占内存
			audio.src = (window.URL || webkitURL).createObjectURL(resultBlob);
			audio.play();

			setTimeout(function () {
				(window.URL || webkitURL).revokeObjectURL(audio.src);
			}, 12000);
		};

		/**上传**/
		function recUpload() {
			var blob
			var loadedAudios = $("#fileInput").get(0).files
			if (loadedAudios.length > 0) {
				blob = loadedAudios[0];
			} else {
				blob = recBlob;
			}
			if (!blob) {
				reclog("请先录音或选择音频，然后停止后再上传", 1);
				return;
			};

			//本例子假设使用原始XMLHttpRequest请求方式，实际使用中自行调整为自己的请求方式
			//录音结束时拿到了blob文件对象，可以用FileReader读取出内容，或者用FormData上传
			var api = "/api/synthesize";

			reclog("开始上传到" + api + "，请求稍后...");

			var reader = new FileReader();
			reader.onloadend = function () {
				var csrftoken = "{{ csrf_token() }}";
				var user_input_text = document.getElementById("user_input_text");
				var input_text = user_input_text.value;
				var postData = new FormData();
				postData.append("text", input_text)
				postData.append("file", blob)
				var sel = document.getElementById("select");
				var path = sel.options[sel.selectedIndex].value;
				if (!!path) {
					postData.append("synt_path", path);
				}

				fetch(api, {
					method: 'post',
					headers: {
						"X-CSRFToken": csrftoken
					},
					body: postData
				}).then(function (res) {
					if (!res.ok) throw Error(res.statusText);
					return res.blob();
				}).then(function (blob) {
					playResult(blob)
				}).catch(function (err) {
					console.log('Error: ' + err.message);
				})
			};
			reader.readAsDataURL(blob);
		};

		//recOpen我们可以选择性的弹一个对话框：为了防止移动端浏览器存在第三种情况：用户忽略，并且（或者国产系统UC系）浏览器没有任何回调
		var showDialog = function () {
			if (!/mobile/i.test(navigator.userAgent)) {
				return;//只在移动端开启没有权限请求的检测
			};
			dialogCancel();

			//显示弹框，应该使用自己的弹框方式
			var div = document.createElement("div");
			document.body.appendChild(div);
			div.innerHTML = (''
				+ '<div class="waitDialog" style="z-index:99999;width:100%;height:100%;top:0;left:0;position:fixed;background:rgba(0,0,0,0.3);">'
				+ '<div style="display:flex;height:100%;align-items:center;">'
				+ '<div style="flex:1;"></div>'
				+ '<div style="width:240px;background:#fff;padding:15px 20px;border-radius: 10px;">'
				+ '<div style="padding-bottom:10px;">录音功能需要麦克风权限，请允许；如果未看到任何请求，请点击忽略~</div>'
				+ '<div style="text-align:center;"><a onclick="waitDialogClick()" style="color:#0B1">忽略</a></div>'
				+ '</div>'
				+ '<div style="flex:1;"></div>'
				+ '</div>'
				+ '</div>');
		};
		var createDelayDialog = function () {
			dialogInt = setTimeout(function () {//定时8秒后打开弹窗，用于监测浏览器没有发起权限请求的情况，在open前放置定时器利于收到了回调能及时取消（不管open是同步还是异步回调的）
				showDialog();
			}, 8000);
		};
		var dialogInt;
		var dialogCancel = function () {
			clearTimeout(dialogInt);

			//关闭弹框，应该使用自己的弹框方式
			var elems = document.querySelectorAll(".waitDialog");
			for (var i = 0; i < elems.length; i++) {
				elems[i].parentNode.removeChild(elems[i]);
			};
		};
//recOpen弹框End
	</script>

	<!--以下这坨可以忽略-->
	<script>
		function reclog(s, color) {
			var now = new Date();
			var t = ("0" + now.getHours()).substr(-2)
				+ ":" + ("0" + now.getMinutes()).substr(-2)
				+ ":" + ("0" + now.getSeconds()).substr(-2);
			var div = document.createElement("div");
			var elem = document.querySelector(".reclog");
			elem.insertBefore(div, elem.firstChild);
			div.innerHTML = '<div style="color:' + (!color ? "" : color == 1 ? "#327de8" : color == 2 ? "#5da1f5" : color) + '">[' + t + ']' + s + '</div>';
		};
		window.onerror = function (message, url, lineNo, columnNo, error) {
			reclog('<span style="color:red">【Uncaught Error】' + message + '<pre>' + "at:" + lineNo + ":" + columnNo + " url:" + url + "\n" + (error && error.stack || "不能获得错误堆栈") + '</pre></span>');
		};
	</script>

	<script>
		if (/mobile/i.test(navigator.userAgent)) {
			//移动端加载控制台组件
			var elem = document.createElement("script");
			elem.setAttribute("type", "text/javascript");

			elem.setAttribute("src", "{{ url_for('static',filename='js/eruda.min.js') }}");
			document.body.appendChild(elem);
			elem.onload = function () {
				eruda.init();
			};
		};
	</script>


	<style>
		body {
			word-wrap: break-word;
			background: #f5f5f5 center top no-repeat;
			background-size: auto 680px;
		}

		pre {
			white-space: pre-wrap;
		}

		a {
			text-decoration: none;
			color: #327de8;
		}

		a:hover {
			color: #5da1f5;
		}

		.main {
			max-width: 700px;
			margin: 0 auto;
			padding-bottom: 80px
		}

		.mainBox {
			margin-top: 12px;
			padding: 12px;
			border-radius: 6px;
			background: #fff;
			box-shadow: 2px 2px 3px #aaa;
		}


		.btns button {
			display: inline-block;
			cursor: pointer;
			border: none;
			border-radius: 3px;
			background: #5698c3;
			color: #fff;
			padding: 0 15px;
			margin: 3px 10px 3px 0;
			width: 70px;
			line-height: 36px;
			height: 36px;
			overflow: hidden;
			vertical-align: middle;
		}

		.btns #upload {
			background: #5698c3;
			color: #fff;
			width: 100px;
			height: 42px;
		}

		.btns button:active {
			background: #5da1f5
		}

		.btns button:hover {
			background: #5da1f5
		}
		.pd {
			padding: 0 0 6px 0;
		}

		.lb {
			display: inline-block;
			vertical-align: middle;
			background: #327de8;
			color: #fff;
			font-size: 14px;
			padding: 2px 8px;
			border-radius: 99px;
		}

		#fileInput {
			width: 0.1px;
			height: 0.1px;
			opacity: 0;
			overflow: hidden;
			position: absolute;
			z-index: -1;
		}
		#fileInput + label {
			padding: 0 15px;
			border-radius: 4px;
			color: white;
			background-color: #5698c3;
			display: inline-block;
			width: 70px;
			line-height: 36px;
			height: 36px;
		}
		#fileInput + label {
			cursor: pointer; /* "hand" cursor */
		}
		#fileInput:focus + label,
		#fileInput + label:hover {
			background-color: #5da1f5;
		}

		.box select {
			background-color: #5698c3;
			color: white;
			padding: 8px;
			width: 120px;
			border: none;
			border-radius: 4px;
			font-size: 0.5em;
			outline: none;
			margin: 3px 10px 3px 0;
		}

		.box::before {
			content: "\f13a";
			position: absolute;
			top: 0;
			right: 0;
			width: 20%;
			height: 100%;
			text-align: center;
			font-size: 28px;
			line-height: 45px;
			color: rgba(255, 255, 255, 0.5);
			background-color: rgba(255, 255, 255, 0.1);
			pointer-events: none;
		}

		.box:hover::before {
			color: rgba(255, 255, 255, 0.6);
			background-color: rgba(255, 255, 255, 0.2);
		}

		.box select option {
			padding: 30px;
		}
	</style>

</body>

</html>